# This file is part of Neuroinfo Toolkit.
#
# Neuroinfo Toolkit is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Neuroinfo Toolkit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Neuroinfo Toolkit.  If not, see <http://www.gnu.org/licenses/>.

import os
from shutil import copy2, move
from neuro.base import Object
import neuro.filesystem as filesystem
from neuro.exceptions import IOException
from neuro.exceptions import FileExistsException
from neuro.exceptions import IllegalArgumentException

class Resource(Object):
	'''
	Base resource model
	'''

	def __init__(self, filename):
		'''
		Constructor ::
		
			resource = Resource("/path/to/file.txt")
			
		:param filename:
		:type filename: str
		'''
		self._filename = None
		self._size = None
		self._type = None
		
		if(filename != None):
			self.importFile(filename)
		else:
			raise IllegalArgumentException("Must provide a filename to Resource constructor")
	
	def importFile(self, filename):
		'''
		Import resource from URI
				
		:param filename:
		:type filename: str
		:raises: IOException
		'''
		filename = filesystem.canonical(filename)
		
		if(not filesystem.isReadable(filename)):
			raise IOException(IOException.READ, filename)
		
		self._filename = filename

	def getFilename(self):
		'''
		Retrieve filename ::
		
			resource = Resource("/path/to/file.txt")
			fname = resource.getFilename()
		
		:rtype: str
		'''
		return self._filename
		
	def getSize(self):
		'''
		Retrieve resource size ::
		
			resource = Resource("/path/to/file.txt")
			size = resource.getSize()
		
		:rtype: int
		'''
		return self._size
		
	def getMimeType(self):
		'''
		Return resource type (MIME) ::
		
			resource = Resource("/path/to/file.txt")
			mime_type = resource.getMimeType()
		
		:rtype: str
		'''
		return self._type
	
	@staticmethod
	def checkFormat(filename):
		'''
		Overridable format checker method
		
		:param filename:
		:type filename: str
		:raises: :class:`~neuro.exceptions.FileNotFoundExcpeiton`, 
				 :class:`~neuro.exceptions.IOException`
		'''
		filename = filesystem.canonical(filename)
		
		if(not filesystem.isReadable(filename)):
			raise IOException(IOException.READ, filename)
		
	def copyTo(self, dir, prefix=None):
		'''
		Copy file to a different location ::
		
			resource = Resource("/path/to/file.txt")
			resource.copyTo("/tmp", "mycopy")
			
		:param dir: Copy to this directory
		:type dir: str
		:param prefix: New filename without extension
		:type prefix: str
		:raises: :class:`~neuro.exceptions.IOException`,
				 :class:`~neuro.exceptions.FileExistsException`
		'''
		dir = filesystem.canonical(dir)
		
		if(not filesystem.exists(dir)):
			filesystem.mkdir(dir)
		elif(not filesystem.isWritable(dir)):
			raise IOException(IOException.WRITE, dir)
		
		## --- construct filename if necessary
		if(prefix != None):
			if(not isinstance(prefix, basestring)):
				raise IllegalArgumentException("If defined, prefix must be an instance of str")

			prefix = prefix.strip()
			
			if(prefix == ""):
				raise IllegalArgumentException("If defined, prefix cannot be empty")
			
			extension = self.getExtension()
			
			name = prefix + extension
		else:
			name = filesystem.basename(self._filename)

		newFile = os.path.join(dir, name)

		## --- check if we're about to overwrite an existing file
		if(filesystem.exists(newFile)):
			raise FileExistsException(newFile)
		
		## --- copy2 copies file, permission bits and metadata 
		copy2(self._filename, newFile)
		
	def moveTo(self, dir, prefix):
		'''
		Move file to a different location ::
		
			resource = Resource("/path/to/file.txt")
			resource.moveTo("/tmp", "moved")
		
		:param dir: Copy to this directory
		:type dir: str
		:param prefix: New filename, without extension
		:type prefix: str
		:raises: :class:`~neuro.exceptions.IOException`,
				 :class:`~neuro.exceptions.FileExistsException`
		'''
		dir = filesystem.canonical(dir)
		
		if(not filesystem.exists(dir)):
			filesystem.mkdir(dir)
		elif(not filesystem.isWritable(dir)):
			raise IOException(IOException.WRITE, dir)
		
		## --- construct filename if necessary
		if(prefix != None):
			if(not isinstance(prefix, basestring)):
				raise IllegalArgumentException("Prefix must be an instance of str")

			prefix = prefix.strip()
			
			if(prefix == ""):
				raise IllegalArgumentException("Prefix cannot be empty")
			
			extension = self.getExtension()
			
			name = prefix + extension
		else:
			name = filesystem.basename(self._filename)

		newFile = os.path.join(dir, name)

		## --- check if we're about to overwrite an existing file
		if(filesystem.exists(newFile)):
			raise FileExistsException(newFile)
				
		move(self._filename, newFile)
		
	def getExtension(self):
		'''
		Retrieve resource extension ::
		
			resource = Resource("/path/to/file.txt")
			ext = resource.getExtension()
			
		:rtype: str
		'''
		return filesystem.getExtension(self._filename)
	
	def delete(self):
		'''
		Delete this resource ::
		
			resource = Resource("/path/to/file.txt")
			resource.delete()
		
		:raises: :class:`~neuro.exceptions.IOException`
		'''
		filesystem.delete(self._filename)